package com.base.app_core.base.impl;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;

import com.base.app_core.base.TreeElement;

import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by xiangheng.song on 2017/3/2.
 */

public abstract class BaseExpandableAdapter<T extends TreeElement<V>, V, G, C> extends BaseExpandableListAdapter {

    protected List<T> list;
    private Context context;

    private static final Map<Class<?>, Constructor<? extends BaseViewHolder>> GROUP_VIEW_HOLDER_MAPPING = new HashMap<>();
    private static final Set<Class> GROUP_INNER_CLASS_SET = new HashSet<>();

    private static final Map<Class<?>, Constructor<? extends BaseViewHolder>> CHILD_VIEW_HOLDER_MAPPING = new HashMap<>();
    private static final Set<Class> CHILD_INNER_CLASS_SET = new HashSet<>();

    public BaseExpandableAdapter(Context context, List<T> list){
        this.context = context;
        this.list = list;
    }

    public Context getContext() {
        return context;
    }

    public BaseExpandableAdapter(Context context, T... array){
        this.context = context;
        this.list = Arrays.asList(array);
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public void notifyDataSetChanged(List<T> list) {
        this.list=list;
        super.notifyDataSetChanged();
    }

    @Override
    public int getGroupCount() {
        return list.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        List<V> children = list.get(groupPosition).getChildren();
        if(children == null){
            return 0;
        }
        return children.size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return list.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return list.get(groupPosition).getChildren().get(childPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

	/**
	 * 计算当前group节点是第几个
	 * @param listView
	 * @param groupPosition
	 * @return
	 */
	public int getPosition(ExpandableListView listView, int groupPosition){
		return getPosition(listView, groupPosition, -1);
	}

    /**
     * 计算当前选中的节点是第几个
     * @param listView
     * @param groupPosition
     * @param childPosition
     * @return
     */
    public int getPosition(ExpandableListView listView, int groupPosition, int childPosition){
        int numberOfOpenedChildren = 0;
        for(int i = 0; i < groupPosition; i++){
            if(listView.isGroupExpanded(i)){
                numberOfOpenedChildren += getChildrenCount(i);
            }
        }
        return numberOfOpenedChildren + groupPosition + childPosition + 1;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public View getGroupView(int groupPosition, boolean expanded, View view, ViewGroup viewGroup) {
        Object holder;
        if(view == null){
            view = View.inflate(context, getGroupItemLayoutId(), null);
            holder = createGroupViewHolder(this, view);
            view.setTag(holder);
        }else{
            holder = view.getTag();
        }

        T item = (T) getGroup(groupPosition);
        fillGroupData(groupPosition, item, (G) holder, expanded);
        return view;
    }

    protected abstract void fillGroupData(int groupPosition, T group, G holder, boolean expanded);
    protected abstract void fillChildData(int groupPosition, int childPosition, V child, C holder);

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean expanded, View view, ViewGroup viewGroup) {
        Object holder;
        if(view == null){
            view = View.inflate(context, getChildItemLayoutId(), null);
            holder = createChildViewHolder(this, view);
            view.setTag(holder);
        }else{
            holder = view.getTag();
        }

        V item = (V) getChild(groupPosition, childPosition);
        fillChildData(groupPosition, childPosition, item, (C) holder);
        return view;
    }

    public T getGroupItem(int groupPosition){
        return (T) getGroup(groupPosition);
    }

    public V getChildItem(int groupPosition, int childPosition){
        return (V) getChild(groupPosition, childPosition);
    }

    protected abstract int getGroupItemLayoutId();

    protected abstract int getChildItemLayoutId();

    @Override
    public boolean isChildSelectable(int i, int childPosition) {
        return true;
    }

    private static Object createGroupViewHolder(BaseExpandableAdapter adapter, View view) {
        return createViewHolder(adapter, view, GROUP_VIEW_HOLDER_MAPPING, GROUP_INNER_CLASS_SET, 2);
    }

    private static Object createChildViewHolder(BaseExpandableAdapter adapter, View view) {
        return createViewHolder(adapter, view, CHILD_VIEW_HOLDER_MAPPING, CHILD_INNER_CLASS_SET, 3);
    }

    private static Object createViewHolder(BaseExpandableAdapter adapter, View view
            , Map<Class<?>, Constructor<? extends BaseViewHolder>> viewHolderMapping
            , Set<Class> innerClassSet, int viewHolderTypePosition) {
        Class<?> adapterClass = adapter.getClass();
        Constructor constructor = viewHolderMapping.get(adapterClass);
        boolean isInnerClass = innerClassSet.contains(adapterClass);
        if(constructor == null){
            try {
                Class<?> viewHolderClass = (Class<?>) ((ParameterizedType) adapterClass.getGenericSuperclass()).getActualTypeArguments()[viewHolderTypePosition];
                isInnerClass = viewHolderClass.getName().contains("$");
                if(isInnerClass){
                    // 内部类构造方法第一个参数是外部类的对象
                    constructor = viewHolderClass.getConstructor(adapter.getClass(), View.class);
                    innerClassSet.add(adapterClass);
                }else{
                    constructor = viewHolderClass.getConstructor(View.class);
                }
                viewHolderMapping.put(adapterClass, constructor);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
        try {
            if(isInnerClass){
                return constructor.newInstance(adapter, view);
            }
            return constructor.newInstance(view);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
